#include "graham_aux.h"

const double PI = 3.14159265;

/// ////////////////////////////////// ///
/// ///////// Classe "Ponto" ///////// ///
/// ////////////////////////////////// ///
Ponto::Ponto(double a,double b) {
    this->x = a;
    this->y = b;
    val = 0;
}

void Ponto::setX(double x) {
    this->x = x;
}
void Ponto::setY(double y) {
    this->y = y;
}

void Ponto::setVal(double x) {
    val = x;
}

Ponto Ponto::operator-(Ponto b) {
    Ponto p;
    p.x = this->x - b.x;
    p.y = this->y - b.y;
    return p;
}

Ponto Ponto::operator*(double x) {
    Ponto r;
    r.x = x * this->x;
    r.y = x * this->y;
    return r;
}
/// ////////////////////////////////// ///
/// ///////// Classe "Ponto" ///////// ///
/// ////////////////////////////////// ///

QVector<Ponto> gerarPontosAleatorio(int n,double maxX,double maxY) {
    srand(time(NULL));
    QVector<Ponto> pontos(0);
    int x,y,X,Y;
    X = (int) maxX;
    Y = (int) maxY;
    Ponto *p;

    for (int i = 0; i < n; i++) {
        x = rand() % (1 + X - 20);
        y = rand() % (1 + Y - 20);
        p = (new Ponto(x,y));
        pontos.append(*p);
        delete p;
    }
    return pontos;
}

void imprimePontos(QVector<Ponto> pts) {
    for(int i = 0; i < pts.size(); i++) {
        std::cout << "(" << pts.at(i).x << "," << pts.at(i).y << ") -> " << pts.at(i).val << std::endl;
    }
}

Ponto baricentro(QVector<Ponto> pts) {
    double sumX = 0;
    double sumY = 0;
    Ponto p;
    for(int i = 0; i < pts.size(); i++) {
        sumX += pts.at(i).x;
        sumY += pts.at(i).y;
    }
    p.x = sumX/pts.size();
    p.y = sumY/pts.size();
    return p;
}

Ponto normaliza(Ponto p) {
    Ponto r;
    double sqrX = p.x * p.x;
    double sqrY = p.y * p.y;
    double norma = sqrt(sqrX + sqrY);
    r.x = p.x/norma;
    r.y = p.y/norma;
    return r;
}

double angulo(Ponto a,Ponto b) {
    return acos((a.x * b.x) + (a.y * b.y));
}

QVector<Ponto> valoraPontos(QVector<Ponto> pts) {
    QVector<Ponto> r(0);
    Ponto c = baricentro(pts);
    Ponto temp,ref;
    Ponto *p;

    Ponto pxMax;
    double xMax = 0;
    int mIndex = 0;
    for(int i = 0; i < pts.size(); i++) {
        if (pts.at(i).x > xMax) {
            pxMax = pts.at(i);
            xMax = pts.at(i).x;
            mIndex = i;
        }
    }

    ref = normaliza(pxMax - c);


    for(int i = 0; i < pts.size(); i++) {
        temp = pts.at(i);
        temp = normaliza(temp - c);

        p = new Ponto(pts.at(i).x,pts.at(i).y);
        p->setVal(angulo(ref,temp));
        if(i == mIndex) p->val = 0;
        //if (pts.at(i).y > c.y) //se o angulo for negativo, inverte!
        if(distanceFromLine((pts.at(i)),c,pxMax) > 0)
            p->setVal(2*PI - p->val);
            //p->val = -1;

        r.append(*p);
        delete p;
    }

    return r;
}
bool compare (Ponto i,Ponto j) {
    return i.val < j.val;
}
/*
std::vector<Ponto> geraPolEstrelado(QVector<Ponto> pts) {
    QVector<Ponto> pValorados = valoraPontos(pts);
    std::vector<Ponto> pontos = pValorados.toStdVector();
    sort(pontos.begin(),pontos.end(),compare);

    return pontos;
}
*/
double scaleProd(Ponto a,Ponto b) {
    double p = 0;
    p = a.x* b.x + a.y * b.y;
    return p;
}
double distanceFromLine(Ponto p,Ponto p1,Ponto p2) {
    Ponto n = p2 - p1;
    n = normaliza(n);
    Ponto ap = p1 - p;
    qreal prod = scaleProd(ap,n);
    Ponto vectD = ap - (n*prod);
    Ponto nVectD = normaliza(vectD);

    qreal sign = -1;
    if ((float)nVectD.x == (float)n.y && (float)nVectD.y == (float)(-1 * n.x)) {
        sign = 1;
    }

    return sign ;
}

bool compEqual(Ponto i, Ponto j) {
    return i.val == j.val;
}

double norma(Ponto i) {
    double r = sqrt(i.x*i.x + i.y*i.y);
    return r;
}

std::vector<Ponto> desempataPontos(std::vector<Ponto> pts,Ponto c) {
    std::vector<Ponto> pontos = pts;
    std::vector<Ponto>::iterator it,first,end,max;
    double tx=0.0,ty=0.0,tv=0.0;
    Ponto *pTemp;
    int fIndex = 0;
    double tempVal,maxNorma,tempNorma;
    //int count = 1;

    while((it = adjacent_find(pontos.begin(),pontos.end(),compEqual)) != pontos.end()) {
        fIndex = it - pontos.begin();
        first = it;
        tempVal = (*it).val;
        maxNorma = norma((*it) - c);
        while((*it).val == tempVal || it == pontos.end()) {
            tempNorma = norma((*it) - c);
            if (tempNorma >= maxNorma) {
                tx = (*it).x;
                ty = (*it).y;
                tv = (*it).val;
                maxNorma = tempNorma;
            }
            it++;
        }
        end = it;

        pontos.erase(first,end);
        pTemp = new Ponto;
        pTemp->x = tx;
        pTemp->y = ty;
        pTemp->val = tv;
        pontos.insert(pontos.begin() + fIndex,*pTemp);
    }

    return pontos;
}

std::vector<Ponto> geraPolEstrelado(QVector<Ponto> pts) {
    Ponto c = baricentro(pts);
    QVector<Ponto> pValorados = valoraPontos(pts);
    std::vector<Ponto> pontos = pValorados.toStdVector();
    sort(pontos.begin(),pontos.end(),compare);
    pontos = desempataPontos(pontos,c);
    return pontos;
}

double prodVet(Ponto a,Ponto b,Ponto c) {
    double r = (a.x*b.y - b.x*a.y) + (b.x*c.y - c.x*b.y) + (c.x*a.y - a.x*c.y);
    //double r = (a.x*b.y + b.x*a.y) - (b.x*c.y + c.x*b.y) - (c.x*a.y + a.x*c.y);
    return r;
}
